package com.jackstraw.jack_base.remote_command.service;

import com.jackstraw.jack_base.remote_command.entity.Conn;
import com.jackstraw.jack_base.remote_command.entity.JackDictProperties;
import com.jackstraw.jack_base.util.FileUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.apache.commons.text.StringSubstitutor;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Jsch的util
 * <p>
 * 将通过conn切面设置和关闭连接对象，因此规定第一个参数为Conn对象
 */
public interface JschService {

    /**
     * @param conn
     * @param directory  上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param uploadFile 要上传的文件全路径
     */
    void upload(Conn conn, String directory, String uploadFile) throws Exception;

    /**
     * @param conn
     * @param directory 上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param file      要上传的文件
     */
    void upload(Conn conn, String directory, File file) throws Exception;

    /**
     * @param conn
     * @param directory      上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param inputStream    文件的IO流
     * @param uploadFileName 上传文件的名字
     */
    void upload(Conn conn, String directory, InputStream inputStream, String uploadFileName) throws Exception;

    void notUnixUpload(Conn conn, String directory, InputStream inputStream, String uploadFileName) throws Exception;


    /**
     * @param conn
     * @param directory 远程目录
     */
    void mkdir(Conn conn, String directory) throws Exception;

    /**
     * @param conn
     * @param directory 路径
     * @return
     */
    Boolean checkFileExists(Conn conn, String directory) throws Exception;


    /**
     * @param conn
     * @param directory         上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param inputStream       上传文件的IO流
     * @param fileName          上传文件名称
     * @param dictPropertiesMap 替换字符的字典
     * @throws Exception
     */
    void uploadReplace(Conn conn, String directory, InputStream inputStream, String fileName, Map<String, String> dictPropertiesMap) throws Exception;

    /**
     * @param conn
     * @param cmd  命令
     * @return
     */
    String execShell(Conn conn, String cmd) throws Exception;

    /**
     * @param conn
     * @param cmdList 命令集合
     * @return
     */
    List<String> execShell(Conn conn, List<String> cmdList) throws Exception;


    /**
     * @param conn
     * @param cmdList 命令集合
     * @param charset 返回字符集
     * @return
     */
    String execAllShell(Conn conn, List<String> cmdList, Charset charset) throws Exception;

    /**
     * 赋予件可执行权限
     *
     * @param conn
     * @param installPath
     */
    default void chmodInstall(Conn conn, String installPath) throws Exception {
        execShell(conn, "chmod +x  ".concat(installPath));
    }

    /**
     * 赋予文件全部权限
     *
     * @param conn
     * @param installPath
     */
    default void chmodAllInstall(Conn conn, String installPath) throws Exception {
        execShell(conn, "chmod 777  ".concat(installPath));
    }

    /**
     * 替换\r\n为\r
     * @param conn
     * @param installPath
     * @throws Exception
     */
    default void unix(Conn conn, String installPath) throws Exception {
        execShell(conn, "sed -i \"s/\\r//\" ".concat(installPath));
    }

    /**
     * @param conn
     * @param directory         上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param uploadFile        上传文件的路径
     * @param dictPropertiesMap 替换字符的字典
     * @throws Exception
     */
    default void uploadReplace(Conn conn, String directory, String uploadFile, Map<String, String> dictPropertiesMap) throws Exception {
        this.uploadReplace(conn, directory, new File(uploadFile), dictPropertiesMap);
    }

    /**
     * @param conn
     * @param directory         上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param file              要上传的文件全路径
     * @param dictPropertiesMap 替换字符的字典
     */
    default void uploadReplace(Conn conn, String directory, File file, Map<String, String> dictPropertiesMap) throws Exception {
        this.uploadReplace(conn, directory, new FileInputStream(file), file.getName(), dictPropertiesMap);
    }

    /**
     * @param conn
     * @param directory              上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param uploadFile             要上传的文件全路径
     * @param jackDictPropertiesList 替换字符的字典
     * @throws Exception
     */
    default void uploadReplace(Conn conn, String directory, String uploadFile, List<JackDictProperties> jackDictPropertiesList) throws Exception {
        this.uploadReplace(conn, directory, new File(uploadFile), jackDictPropertiesList);
    }

    /**
     * @param conn
     * @param directory              上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param file                   要上传的文件
     * @param jackDictPropertiesList 替换字符的字典
     */
    default void uploadReplace(Conn conn, String directory, File file, List<JackDictProperties> jackDictPropertiesList) throws Exception {
        this.uploadReplace(conn, directory, new FileInputStream(file), file.getName(), jackDictPropertiesList);
    }

    /**
     * @param conn
     * @param directory              上传的目录-相对于SFPT设置的用户访问目录， 为空则在SFTP设置的根目录进行创建文件（除设置了服务器全磁盘访问）
     * @param inputStream            上传文件的IO流
     * @param fileName               上传文件名称
     * @param jackDictPropertiesList 替换字符的字典
     * @throws Exception
     */
    default void uploadReplace(Conn conn, String directory, InputStream inputStream, String fileName, List<JackDictProperties> jackDictPropertiesList) throws Exception {
        Map<String, String> dictPropertiesMap = new HashMap<>();
        if (ObjectUtils.isNotEmpty(jackDictPropertiesList)) {
            dictPropertiesMap = jackDictPropertiesList.stream().collect(Collectors.toMap(JackDictProperties::getDictName, JackDictProperties::getDictValue));
        }
        uploadReplace(conn, directory, inputStream, fileName, dictPropertiesMap);
    }


    /**
     * @param inputStream
     * @param dictPropertiesMap 字典
     * @return
     */
    default InputStream replaceInputStream(InputStream inputStream, Map<String, String> dictPropertiesMap) throws Exception {
        StringSubstitutor stringSubstitutor = new StringSubstitutor(dictPropertiesMap);
        String response = FileUtil.IoUtilByHutool.readNote(inputStream);
        response = stringSubstitutor.replace(response);
        return new ByteArrayInputStream(response.getBytes());
    }

    /**
     * 将以\r\n结尾的转换为\n
     *
     * @param inputStream
     * @return
     */
    default InputStream rnTransitionN(InputStream inputStream) {
        String response = FileUtil.IoUtilByHutool.readNote(inputStream);
        response.replace("\r\n", "\n");
        return new ByteArrayInputStream(response.getBytes());
    }


    /**
     * @param conn
     * @param directory 路径
     * @param fileName  文件名称
     * @return
     */
    default Boolean checkFileExists(Conn conn, String directory, String fileName) throws Exception {
        return this.checkFileExists(conn, directory.concat("/").concat(fileName));
    }


    /**
     * @param conn
     * @param cmd  命令
     * @return
     */
    default String execAllShell(Conn conn, String cmd) throws Exception {
        return this.execAllShell(conn, cmd, Charset.defaultCharset());
    }

    /**
     * @param conn
     * @param cmdList 命令集合
     * @return
     */
    default String execAllShell(Conn conn, List<String> cmdList) throws Exception {
        return this.execAllShell(conn, cmdList, Charset.defaultCharset());
    }

    /**
     * @param conn
     * @param cmd         命令
     * @param charsetName 字符集名称
     * @return
     */
    default String execAllShell(Conn conn, String cmd, String charsetName) throws Exception {
        return execAllShell(conn, cmd, Charset.forName(charsetName));
    }

    /**
     * @param conn
     * @param cmd     命令
     * @param charset 返回字符集
     * @return
     */
    default String execAllShell(Conn conn, String cmd, Charset charset) throws Exception {
        List<String> cmdList = new ArrayList<>();
        cmdList.add(cmd);
        return this.execAllShell(conn, cmdList, charset);
    }

    /**
     * @param conn
     * @param cmdList     命令集合
     * @param charsetName 字符集名称
     * @return
     */
    default String execAllShell(Conn conn, List<String> cmdList, String charsetName) throws Exception {
        return execAllShell(conn, cmdList, Charset.forName(charsetName));
    }

}
